/**
 * @fileoverview
 * This script defines utilities for interacting with data from www.fly-ted.org.
 * @author <a href="http://purl.org/net/aliman">Alistair Miles</a>, Graham Klyne, Jun Zhao
 * @version $Revision:538 $ on $Date: 2008-08-27 09:08:41 +0100 (Wed, 27 Aug 2008) $ by $Author: aliman $
 * @requires YAHOO.util.Connect
 * @requires flyui.flyted.GeneNameMappings
 * TODO license terms
 */

// create a namespace if not already defined
flyui.namespace("flyui.flyted");


/**
 * TODO doc me
 */
flyui.flyted.Service = function (endpointURL) {
	/**
	 * @private
	 */
	this._endpointURL = endpointURL;
}


/**
 * TODO doc me
 */
flyui.flyted.Service.prototype.query = function (query, success, failure){
	log("flyui.flyted.Service :: query request: "+query);
	
	// define the callback object
	var callback = {
		success: success,
		failure: failure
	};
			
	var url = this._buildURL(query);
	
	log("flyui.flybase.Service :: make the request to "+url);
	var request = YAHOO.util.Connect.asyncRequest("GET", url, callback, null);
	
};


/**
 * TODO doc me
 */
flyui.flyted.Service.prototype.findImagesByFlytedGeneName = function( geneName, success, failure ) {
	log("flyui.flyted.Service :: findImagesByFlytedGeneName: "+geneName);
	
	var successAdapter = flyui.flyted.Service._buildSuccessAdapterForFindImagesByFlytedGeneName(success);

	var query = flyui.flyted.Service._buildQueryForFindImagesByFlytedGeneName(geneName);
	
	this.query(query, successAdapter, failure);
	
};


/**
 * TODO doc me
 */
flyui.flyted.Service.prototype.findImagesByUnionOfFlytedGeneNames = function( geneNames, success, failure ) {
	log("flyui.flyted.Service :: findImagesByUnionOfFlytedGeneNames: "+geneNames);
	
	if (geneNames.length == 0) {
		
		log("handle no gene names, call back immediately with empty array", "debug");
		success([]);
		
	} 
	else if (geneNames.length == 1) {
		
		log("handle single name, passing throught to findImagesByFlytedName...", "debug");
		this.findImagesByFlytedGeneName(geneNames[0], success, failure);
		
	}
	else {
		
		log("handle multiple gene names", "debug");

		// N.B. can re-use the same success adapter ...
		var successAdapter = flyui.flyted.Service._buildSuccessAdapterForFindImagesByFlytedGeneName(success);
	
		// ... but need a different query
		var query = flyui.flyted.Service._buildQueryForFindImagesByUnionOfFlytedGeneNames(geneNames);
		
		this.query(query, successAdapter, failure);

	}		
}


/**
 * Find images in the FlyTED database for the given flybase gene ID.
 * @param {String} flybaseGeneID the flybase gene ID e.g. FBgn012345
 * @param {Function} success the success case callback function, which must accept an array of 
 * flyui.flyted.Image objects
 * @param {Function} failure the failure case callback function, which must accept a YUI HTTP response object
 */
flyui.flyted.Service.prototype.findImagesByFlybaseGeneID = function( flybaseGeneID, success, failure) {
	log("flyui.flyted.Service :: findImagesByFlybaseGeneID: "+flybaseGeneID);
	
	var flytedNames = flyui.flyted.Service.mapFlybaseIDToFlytedGeneName(flybaseGeneID);
	log("flyui.flyted.Service.findImagesByFlybaseGeneID :: mapped gene names: "+flytedNames, "debug");
	
	this.findImagesByUnionOfFlytedGeneNames(flytedNames, success, failure);
	
}


/**
 * Return a success case request handler for the findImagesByFlytedGeneName method.
 * @param {Function} adaptedSuccess the function to call with an array of Image objects
 * @return a success callback to use in the HTTP request
 * @type Function
 * @private
 * @static
 */
flyui.flyted.Service._buildSuccessAdapterForFindImagesByFlytedGeneName = function( adaptedSuccess ) {

	return function(responseObject) {
		
		log("response received: "+responseObject.status, "debug");
		
		// try parsing response text as json
		log("parsing response: "+responseObject.responseText, "debug");
		var resultSet = YAHOO.lang.JSON.parse(responseObject.responseText);
		
		// convert result set to an array of genes
		var images = flyui.flyted.Image.newInstancesFromSPARQLResults(resultSet);
		
		// complete the request
		adaptedSuccess(images);
	}

};


/**
 * Build a SPARQL query to retrieve images by flyted gene name.
 * @param {String} anyName the gene name to search by
 * @return a SPARQL query
 * @type String
 * @private
 * @static
 */
flyui.flyted.Service._buildQueryForFindImagesByFlytedGeneName = function( geneName ) {

	var prefixes = 	"PREFIX dro: <http://imageweb.zoo.ox.ac.uk/drosophila#> " +
					"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> " +
					"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> ";
					
	var body = 		"SELECT * WHERE { " +
						"?fullImageURL " +
							"dro:genename '"+geneName+"' ; " +
							"dro:genename ?geneName ; " +
							"dro:thumbnail ?thumbnailURL; " +
							"rdfs:seeAlso ?flytedURL; " +
							"rdfs:label ?caption " +
					"}";
						
	var query = prefixes + body;

	return query;
};


/**
 * Build a SPARQL query to retrieve images by a union of multiple flyted gene names.
 * @param {[String]} anyNames the array of gene names to search by
 * @return a SPARQL query
 * @type String
 * @private
 * @static
 */
flyui.flyted.Service._buildQueryForFindImagesByUnionOfFlytedGeneNames = function( geneNames ) {

	var prefixes = 	"PREFIX dro: <http://imageweb.zoo.ox.ac.uk/drosophila#> " +
					"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> " +
					"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> ";
		
	if (geneNames.length == 0) {
		// this should never happen	
		throw {name:"flyui.flyted.Service.InvalidQueryException", message:"cannot query for union of empty array of gene names"};
	}				
	else if (geneNames.length == 1) {
		return flyui.flyted.Service._buildQueryForFindImagesByFlytedGeneName(geneNames[0]);
	}
	else {
		
		var pattern = function( geneName ) {
			return 	"{ "+
						"?fullImageURL " +
							"dro:genename '"+geneName+"' ; " +
							"dro:genename ?geneName ; " +
							"dro:thumbnail ?thumbnailURL; " +
							"rdfs:seeAlso ?flytedURL; " +
							"rdfs:label ?caption " +
					"} ";
		}
		
		var body = 		"SELECT * WHERE { ";
		body +=			pattern(geneNames[0]);
		for (var i=1; i<geneNames.length; i++) {
			body += 	"UNION ";
			body +=		pattern(geneNames[i]);
		}
		body += 		"}";
		
	}

	var query = prefixes + body;

	return query;
}


/**
 * Construct the request URL for the given SPARQL query.
 * @private
 * @param {String} query a SPARQL query
 * @return a request URL
 * @type String
 */
flyui.flyted.Service.prototype._buildURL = function( query ) {
	return this._endpointURL + "?output=json&query=" + escape(query);
};


/**
 * Map a string to a list of strings via a supplied mapping table,
 * returnimng an empty list if the supplied key is not present. 
 * @param {String} key 		a key value to be mapped
 * @param {Object} table 	an object with members that are arrays of strings
 * @return an array of string values, which may be empty if the given key is not found
 * @type [String]
 */
flyui.flyted.Service.mapNameUsingTable = function ( key, table ) {
	var result = table[key];
	if (typeof result == "undefined" || !result ) {
		result = [];
	}
	return result;
}


/**
 * Map a Flybase ID to zero, one or more FlyTED gene names.  Note that if there is no known mapping
 * for the supplied gene name, an empty (zero-length) array is returned.
 * @param {String} a Flybase ID
 * @return an array of FlyTED gene names that return data corresponding to the given Flybase ID
 * @type [String]
 */
flyui.flyted.Service.mapFlybaseIDToFlytedGeneName = function( flybaseID ) {
	
	return flyui.flyted.Service.mapNameUsingTable(flybaseID, flyui.flyted.GeneNameMappings.flybaseToFlyted);
	
}


/**
 * Map a Flybase ID to zero, one or more FlyTED gene names.  Note that if there is no known mapping
 * for the supplied Flybase ID, an empty (zero-length) array is returned.
 * @param {String} a Flybase ID
 * @return an array of FlyTED gene names that return data corresponding to the given Flybase ID
 * @type [String]
 */
flyui.flyted.Service.mapFlytedGeneNameToFlybaseID = function( flytedName ) {

	return flyui.flyted.Service.mapNameUsingTable(flytedName, flyui.flyted.GeneNameMappings.flytedToFlybase);

}


/**
 * TDO doc me
 */
flyui.flyted.Image = function () {
	
	/**
	 * @type String
	 */
	this.fullImageURL = null;
	
	/**
	 * @type String
	 */
	this.thumbnailURL = null;
	
	/**
	 * @type String
	 */
	 
	this.flytedURL = null;
	
	/**
	 * @type String
	 */
	this.caption = null;
	
	/**
	 * @type String
	 */
	this.geneName = null;
}


/**
 * TODO doc me
 */
flyui.flyted.Image.newInstancesFromSPARQLResults = function(resultSet){
	var bindings = resultSet.results.bindings;
	var images = [];
	for (var i=0; i<bindings.length; i++) {
		var binding = bindings[i];
		var image = {};
		for (var variable in binding) {
			image[variable] = binding[variable]["value"];
		}
		images[images.length] = image;
	}
	return images;
}


